package com.atguigu.tingshu.account.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;

import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.model.account.UserAccount;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

	@Autowired
	private UserAccountMapper userAccountMapper;
	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private UserAccountDetailMapper userAccountDetailMapper;
	@Autowired
	private RechargeInfoMapper rechargeInfoMapper;

	/**
	 * 初始化用户账号
	 * @param userId
	 */
	@Override
	public void initUserAccount(String userId) {

		UserAccount userAccount = new UserAccount();
		userAccount.setUserId(Long.parseLong(userId));
		userAccount.setTotalAmount(new BigDecimal("1000")); //总金额
		userAccount.setAvailableAmount(new BigDecimal("1000"));  //可用金额
		userAccount.setTotalIncomeAmount(new BigDecimal("1000"));  //总收入

		//初始化账户信息
		userAccountMapper.insert(userAccount);
	}


	@Override
	public AccountLockResultVo checkAndLock(AccountLockVo accountLockVo) {

		//	声明一个锁的key
		String lockKey = "checkAndLock:" + accountLockVo.getOrderNo();
		//	声明一个数据key
		String dataKey = "account:lock:" + accountLockVo.getOrderNo();
		//	判断是否有锁的key
		Boolean result = this.redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 10, TimeUnit.SECONDS);

		if (!result) {
			//  当前可能查询过了，将数据放入缓存。此时需要从缓存中获取即可.
			AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
			//  判断对象是否为空
			if (accountLockResultVo != null) {
				return accountLockResultVo;
			}
		}
		//  第一次执行查询。
		//  select * from user_account where user_id = 26 and available_amount >= 100 and is_deleted = 0 for update ;
		int check = userAccountMapper.check(accountLockVo.getUserId(), accountLockVo.getAmount());

		//说明用户余额不足
		if (check == 0) {
			//  删除锁
			this.redisTemplate.delete(lockKey);
			//  抛出异常
			throw new GuiguException(ResultCodeEnum.ACCOUNT_LESS);
		}

		//  如果有受影响行数说明有足够的余额.
		int lock = userAccountMapper.lock(accountLockVo.getUserId(), accountLockVo.getAmount());
		//  判断是否锁定成功
		if (lock == 0) {
			//  删除锁
			this.redisTemplate.delete(lockKey);
			//  抛出异常
			throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
		}

		//  给这个对象赋值：并且放入缓存！
		AccountLockResultVo accountLockResultVo = new AccountLockResultVo();
		accountLockResultVo.setAmount(accountLockVo.getAmount());
		accountLockResultVo.setUserId(accountLockVo.getUserId());
		accountLockResultVo.setContent(accountLockVo.getContent());
		//  将数据写入缓存.
		this.redisTemplate.opsForValue().set(dataKey,accountLockResultVo);

		//  调用方法记录上账户资金流动.
		this.addUserAccountDetail(accountLockVo.getUserId(),"锁定："+accountLockVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_LOCK ,accountLockVo.getAmount(), accountLockVo.getOrderNo());
		//  返回数据
		return accountLockResultVo;
	}

	private void addUserAccountDetail(Long userId, String title, String typeLock, BigDecimal amount, String orderNo) {
		//  添加数据
		UserAccountDetail userAccountDetail = new UserAccountDetail(userId,title,typeLock,amount,orderNo);
		userAccountDetailMapper.insert(userAccountDetail);
	}


	@Override
	public void minus(String orderNo) {
		//  声明防止重复key
		String key = "minus:"+orderNo;
		//  声明存储在换成的数据key
		String dataKey = "account:lock:" + orderNo;
		//  判断是否有重复消息的情况.
		Boolean result = this.redisTemplate.opsForValue().setIfAbsent(key, "lock", 10, TimeUnit.SECONDS);
		if (!result){
			return;
		}
		//  第一次消费
		AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
		//  判断
		if (null == accountLockResultVo){
			//  缓存中没有数据
			return;
		}
		//  执行扣减操作
		int minus = userAccountMapper.minus(accountLockResultVo.getUserId(),accountLockResultVo.getAmount());
		if (minus == 0){
			//  删除重复key。
			this.redisTemplate.delete(key);
			return;
		}
		//  记录账户明细.
		this.addUserAccountDetail(accountLockResultVo.getUserId(), accountLockResultVo.getContent(),SystemConstant.ACCOUNT_TRADE_TYPE_MINUS,accountLockResultVo.getAmount(), orderNo);
		//  删除缓存中的扣减信息.
		this.redisTemplate.delete(dataKey);
	}

	@Override
	public void accountUnlock(String orderNo) {
		//  防止重复key
		String key = "unlock:"+orderNo;
		//  数据key
		String dataKey = "account:lock:" + orderNo;
		//  判断key是否存在
		Boolean result = this.redisTemplate.opsForValue().setIfAbsent(key, "lock", 10, TimeUnit.SECONDS);
		if (!result){
			//  重复消费了，直接停止
			return;
		}
		//  从缓存中获取数据
		AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
		if (null == accountLockResultVo){
			//  缓存中没有数据
			return;
		}
		//  执行解锁金额.
		int unlock = userAccountMapper.unlock(accountLockResultVo.getUserId(),accountLockResultVo.getAmount());
		//  判断
		if (unlock == 0){
			//  删除重复key
			this.redisTemplate.delete(key);
			return;
		}
		//  记录日志，删除缓存的dataKey的数据
		this.addUserAccountDetail(accountLockResultVo.getUserId(),accountLockResultVo.getContent(),SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK,accountLockResultVo.getAmount(),orderNo);
		this.redisTemplate.delete(dataKey);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void rechargePaySuccess(String orderNo) {
		//  修改充值记录状态, 增加金额，记录账户明细.
		LambdaQueryWrapper<RechargeInfo> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(RechargeInfo::getOrderNo,orderNo);
		RechargeInfo rechargeInfoQuery = rechargeInfoMapper.selectOne(wrapper);
		rechargeInfoQuery.setRechargeStatus(SystemConstant.ORDER_STATUS_PAID);
		//  根据主键更新.
		rechargeInfoMapper.updateById(rechargeInfoQuery);
		//  增加账户金额
		userAccountMapper.updateAmountByUserId(rechargeInfoQuery.getUserId(), rechargeInfoQuery.getRechargeAmount());
		//  记录账户明细。
		this.addUserAccountDetail(rechargeInfoQuery.getUserId(),"充值:"+rechargeInfoQuery.getRechargeAmount(),SystemConstant.ACCOUNT_TRADE_TYPE_DEPOSIT,rechargeInfoQuery.getRechargeAmount(),orderNo);
	}

	@Override
	public IPage<UserAccountDetail> findUserRechargePage(Page<UserAccountDetail> detailPage, Long userId) {
		LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(UserAccountDetail::getUserId,userId).eq(UserAccountDetail::getTradeType,"1201");
		return userAccountDetailMapper.selectPage(detailPage,wrapper);
	}

	@Override
	public IPage<UserAccountDetail> findUserConsumePage(Page<UserAccountDetail> detailPage, Long userId) {
		LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(UserAccountDetail::getUserId,userId).eq(UserAccountDetail::getTradeType,"1204");
		return userAccountDetailMapper.selectPage(detailPage,wrapper);
	}
}
